package com.winit.config.manager;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.winit.common.util.Constants;
import com.winit.config.model.Config;
import com.winit.config.repository.ConfigOperations;
import com.winit.config.repository.ConfigRepository;
import com.winit.config.repository.DeployZoneRepository;
import com.winit.config.web.ConfigController;
import com.winit.tag.service.ITagService;
import com.winit.template.service.TemplateService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;

/**
 * Created by wangjian on 2017/9/28.
 */
@Component
public class ConfigManager {
    //环境对应的group_code
    private static final String TAG_GROUP_ENV = "ENV";
    //master/base版本对应的version_code
    private static final String CONFIG_VERSION_MASTER = Constants.CONFIG_VERSION_MASTER;
    private static final String CONFIG_VERSION_LATEST = Constants.CONFIG_VERSION_LATEST;
    private static final String CONFIG_TYPE_TAG_APP = Constants.CONFIG_TYPE_APP;
    private static final String CONFIG_TYPE_TAG_RESOURCE = Constants.CONFIG_TYPE_RESOURCE;

    private static Logger logger = LoggerFactory.getLogger(ConfigManager.class);

    @Autowired
    private ConfigRepository configRepository;
    @Autowired
    private ITagService tagService;
    @Autowired
    private DeployZoneRepository deployZoneRepository;
    @Autowired
    private TemplateService templateService;
    @Autowired
    ResourceConfigManager resourceConfigManager;

    @Autowired
    ConfigOperations operations;

    /**
     * {@link ConfigController#getAppConfigs(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String)}专用方法
     *
     * @param config_id
     * @param config_app_tag
     * @param config_env_tag
     * @param config_zone_tag
     */
    public List<Config> getAppConfigs(String config_id, String config_app_tag, String config_env_tag, String config_zone_tag, String content_level) throws Exception{
        List<Config> configs = null;

        Config searchConditions = new Config();
        if (!StringUtils.isEmpty(config_id)) {
            searchConditions.setConfig_id(UUID.fromString(config_id));
        }
        searchConditions.setConfig_app_tag(config_app_tag);
        searchConditions.setConfig_env_tag(config_env_tag);
        searchConditions.setConfig_zone_tag(config_zone_tag);
        searchConditions.setConfig_type_tag(CONFIG_TYPE_TAG_APP);

        configs = configRepository.findByConditions(searchConditions);

        //如果传入了config_id则查指定记录, 查得的结果为空，那么也直接返回
        if ( CollectionUtils.isEmpty(configs) && null != config_id) {
            return configs;
        }
        if ( null != config_id) {   //查出base版本（如果有）合并,
            Config targetConfig = configs.get(0);
            if(null != targetConfig.getConfig_k_v_temp_map()) {
                targetConfig.getConfig_k_v_map().putAll(targetConfig.getConfig_k_v_temp_map());
            }
            if (!CONFIG_VERSION_MASTER.equalsIgnoreCase(targetConfig.getConfig_version())){
                Config search = new Config();
                search.setConfig_app_tag(targetConfig.getConfig_app_tag());
                search.setConfig_resource_tag(CONFIG_TYPE_TAG_APP);
                search.setConfig_version(CONFIG_VERSION_MASTER);
                List<Config> masters = configRepository.findByConditions(search);
                if (!CollectionUtils.isEmpty(masters)){
                    this.mergeConfig(masters.get(0), targetConfig);
                }
            }
            this.renderConfigForApp(targetConfig);
            return configs;
        }
        /***************到了这里请求就没有带config_id了*****************/
        //取master版本,config 里不确定是否存在master，因此直接取master版本
        Config masterCondition = new Config();
        masterCondition.setConfig_app_tag(config_app_tag);
        masterCondition.setConfig_type_tag(CONFIG_TYPE_TAG_APP);
        masterCondition.setConfig_version(CONFIG_VERSION_MASTER);
        List<Config> temp = configRepository.findByConditions(masterCondition);
        if (CollectionUtils.isEmpty(temp)){ //取不到master版本默认系统异常
            logger.error("cannot get master config for app:" + config_app_tag);
            return null;
        }
        Config master = temp.get(0);
        //拿出master版本
//        for (Config temp : configs){
//            if (CONFIG_VERSION_MASTER.equalsIgnoreCase(temp.getConfig_version())){
//                master = temp;
//                break;
//            }
//        }

        List<String> envs = new ArrayList<>();
        List<String> zones = new ArrayList<>();

        //决定参与查询的env
        if (!StringUtils.isEmpty(config_env_tag)) {
            envs.add(config_env_tag);
        } else {          //查找uconfig_tag表中的记录
            envs.addAll(tagService.getTagCodesForGroup(TAG_GROUP_ENV));
        }
        //决定参与查询的zone
        if (!StringUtils.isEmpty(config_zone_tag)) {  //需要查找master版本以获得关联区域
            zones.add(config_zone_tag);

        }else{
            String config_deploy_zone_code = master.getConfig_deploy_zone_code();
            zones.addAll(deployZoneRepository.findOne(config_deploy_zone_code).getDeploy_zones());
        }

        //先合并再render
        return this.mergeAndRenderConfigs(master, configs, envs, zones, content_level);
    }

    private List<Config> mergeAndRenderConfigs(Config master, List<Config> configs, List<String> envs, List<String> zones, String content_level) throws Exception{
        List<Config> rsConfigs = new ArrayList<>();

        Config tempConfig = null;
        Map<String, Object> master_k_v_map = master.getConfig_k_v_map();
        if (!CollectionUtils.isEmpty(master.getConfig_k_v_temp_map())) {
            master_k_v_map.putAll(master.getConfig_k_v_temp_map());
        }
        master.setConfig_k_v_map((JSONObject) master_k_v_map);
        //指定环境与区域的配置是否存在
        boolean exists = false;
        for(String zone : zones){
            for (String env : envs){
                exists = false;
                if (!CollectionUtils.isEmpty(configs)) {
                    for (Config config : configs) {
                        if (env.equalsIgnoreCase(config.getConfig_env_tag()) && zone.equalsIgnoreCase(config.getConfig_zone_tag())) {    //存在配置，则合并
                            //指定配置存在
                            exists = true;
                            tempConfig = mergeConfig(master, config);
                            break;
                        }
                    }
                }
                if (!exists){   //如果指定配置不存在
                    tempConfig = new Config();
                    BeanUtils.copyProperties(master, tempConfig);
                    //设置latest版本相关属性
                    tempConfig.setConfig_k_v_map(JSONObject.parseObject(master_k_v_map.toString()));
                    tempConfig.setConfig_zone_tag(zone);
                    tempConfig.setConfig_env_tag(env);
                    tempConfig.setConfig_version(CONFIG_VERSION_LATEST);
                }

                //进行render
                if(tempConfig.getConfig_k_v_ref_tag() != null){
                    this.renderConfigForApp(tempConfig);
                }
                rsConfigs.add(tempConfig);
            }
        }
        if ("0".equals(content_level)) {
            this.renderConfigForApp(master);
            rsConfigs.add(master);
        }
        return rsConfigs;
    }

    /**
     * 这个方法不会变动master_config的成员，会将master_config中的k_v_map、temp_k_v_map，latest中的temp_k_v_map合并到latest_config中的k_v_map中
     * 将master_config中的ref_tag合并到latest_config中的ref_tag中
     * @param master_config
     * @param latest_config
     * @return      合并配置后的latest_config
     */
    private Config mergeConfig(Config  master_config, Config latest_config) {
        /**合并k_v_map*/
        //新建容器
        Map<String, Object> master_k_v_map = new JSONObject();
        //合并master_config配置k_v_map
        if(!CollectionUtils.isEmpty(master_config.getConfig_k_v_map())) {
            master_k_v_map.putAll(master_config.getConfig_k_v_map());
        }
        //合并master_config配置k_v_temp_map
        if(!CollectionUtils.isEmpty(master_config.getConfig_k_v_temp_map())) {
            master_k_v_map.putAll(master_config.getConfig_k_v_temp_map());
        }
        //合并latest_config配置k_v_map
        if(!CollectionUtils.isEmpty(latest_config.getConfig_k_v_map())) {
            master_k_v_map.putAll(latest_config.getConfig_k_v_map());
        }
        //合并latest_config配置k_v_temp_map
        if(!CollectionUtils.isEmpty(latest_config.getConfig_k_v_temp_map())) {
            master_k_v_map.putAll(latest_config.getConfig_k_v_temp_map());
        }
        latest_config.setConfig_k_v_map((JSONObject) master_k_v_map);

        /**合并ref_tag*/
        Map<String, Object> ref_tag_map = new JSONObject();
        if(!CollectionUtils.isEmpty(master_config.getConfig_k_v_ref_tag())) {
            ref_tag_map.putAll(master_config.getConfig_k_v_ref_tag());
        }
        if(!CollectionUtils.isEmpty(latest_config.getConfig_k_v_ref_tag())) {
            ref_tag_map.putAll(latest_config.getConfig_k_v_ref_tag());
        }
        latest_config.setConfig_k_v_ref_tag((JSONObject)ref_tag_map);
        return latest_config;
    }


    private void renderConfigForApp(Config config) throws Exception{
        JSONObject ref_tags_json = config.getConfig_k_v_ref_tag();
        if (null == ref_tags_json){
            return;
        }
        Map<String, Object> ref_tags = ref_tags_json;

        if (CollectionUtils.isEmpty(ref_tags) ){
            return;
        }
        Map<String, Object> k_v_map = config.getConfig_k_v_map();
        Iterator<String> it = ref_tags.keySet().iterator();
        String ref_tag_key = null;
        while (it.hasNext()){
            ref_tag_key = it.next();
            String ref_tag_value = (String) ref_tags.get(ref_tag_key);
            Config searchConditions = new Config();
            searchConditions.setConfig_env_tag(config.getConfig_env_tag());
            searchConditions.setConfig_zone_tag(config.getConfig_zone_tag());
            searchConditions.setConfig_app_tag(config.getConfig_app_tag());
            searchConditions.setConfig_resource_tag(ref_tag_value);
            searchConditions.setConfig_type_tag(CONFIG_TYPE_TAG_RESOURCE);
            List<Config> resources  = configRepository.findByConditions(searchConditions);
            if (CollectionUtils.isEmpty(resources)){
                //打出error日志
                logger.error("not resource found for: env--" + config.getConfig_env_tag() + ",zone--" + config.getConfig_zone_tag() + ",app--" + config.getConfig_app_tag() + ",resource--" + ref_tag_value);
                continue;
            }
            Config resource  = resources.get(0);

            JSONObject ref_id_json = resource.getConfig_k_v_ref_id();
            if (null != ref_id_json){
                this.renderForResource(resource);
            }
            k_v_map.put(ref_tag_key, templateService.render((String) k_v_map.get(ref_tag_key), resource.getConfig_k_v_map()));
        }
        config.setConfig_k_v_map((JSONObject) JSON.toJSON(k_v_map));
    }

    private void renderForResource(Config resource) throws Exception{
        resourceConfigManager.getResourceAfterRander(resource);
        resource.setConfig_k_v_map(resource.getK_v_preview_map());


    }

    public List<Config> getAppList(String appTag) {
        return operations.getMasterConfig(appTag);
    }

    /**
     * for {@link ConfigController#getBootConfForApp(java.lang.String, java.lang.String, java.lang.String)}
     * @param config_app_tag
     * @param config_env_tag
     * @param config_zone_tag
     * @return
     */
    public Map getBootConfForApp(String config_app_tag, String config_env_tag, String config_zone_tag) throws Exception {
        if (StringUtils.isEmpty(config_app_tag) || StringUtils.isEmpty(config_env_tag) || StringUtils.isEmpty(config_zone_tag)){
            return null;
        }
        /* 先把master拿出来 */
        Config master = null;
        Config searchCondition = new Config();
        searchCondition.setConfig_type_tag(CONFIG_TYPE_TAG_APP);
        searchCondition.setConfig_app_tag(config_app_tag);
        searchCondition.setConfig_version(CONFIG_VERSION_MASTER);
        List<Config> temp = configRepository.findByConditions(searchCondition);
        if(CollectionUtils.isEmpty(temp)){
            logger.error("no master config found for app:" + config_app_tag);
            return null;
        }
        master = temp.get(0);
        Config optConfig = null;
        Map<String, Object> rsMap = new HashMap<String, Object>();
        searchCondition.setConfig_app_tag(config_app_tag);
        searchCondition.setConfig_env_tag(config_env_tag);
        searchCondition.setConfig_zone_tag(config_zone_tag);
        searchCondition.setConfig_type_tag(CONFIG_TYPE_TAG_APP);
        searchCondition.setConfig_version(CONFIG_VERSION_LATEST);
        temp = configRepository.findByConditions(searchCondition);
        if ((!CollectionUtils.isEmpty(temp)) && temp.size() > 1){
            logger.error("more than one configs of latest version found for app---" + config_app_tag + ", env---"  + config_env_tag + ", zone---" + config_zone_tag);
        }
        JSONObject k_v_map = new JSONObject();
        if (!CollectionUtils.isEmpty(master.getConfig_k_v_map())) {
            k_v_map.putAll(master.getConfig_k_v_map());
        }
        JSONObject ref_tags = new JSONObject();
        if (!CollectionUtils.isEmpty(master.getConfig_k_v_ref_tag())) {
            ref_tags.putAll(master.getConfig_k_v_ref_tag());
        }
        if (!CollectionUtils.isEmpty(temp)){
            Config latest = temp.get(0);
            if (!CollectionUtils.isEmpty(latest.getConfig_k_v_map())) {
                k_v_map.putAll(latest.getConfig_k_v_map());
            }
            if (!CollectionUtils.isEmpty(latest.getConfig_k_v_ref_tag())) {
                ref_tags.putAll(latest.getConfig_k_v_ref_tag());
            }
            optConfig = latest;
        }else{
            optConfig = master;
//            BeanUtils.copyProperties(master, optConfig);
            optConfig.setConfig_version(CONFIG_VERSION_LATEST);
            optConfig.setConfig_env_tag(config_env_tag);
            optConfig.setConfig_zone_tag(config_zone_tag);
            optConfig.setConfig_type_tag(CONFIG_TYPE_TAG_APP);
        }
        optConfig.setConfig_k_v_map(k_v_map);
        optConfig.setConfig_k_v_ref_tag(ref_tags);
        this.renderConfigForApp(optConfig);
        return optConfig.getConfig_k_v_map();
    }


}
